#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cerrno>
#include <ctime>

#define THREAD_NUM 5
#define task_t int

#define get_seed() srand((int)time(nullptr) & 0x23415 ^ 1235)

//////////////////////////////////////////////任务集////////////////////////////////////////////////
void download_task()
{
    std::cout << "the proc downloading" << std::endl;
}

void remove_task()
{
    std::cout << "the proc removing" << std::endl;
}

void create_task()
{
    std::cout << "the proc creating" << std::endl;
}

//加载任务
void load_task(std::vector<std::function<void()>> *out)
{
    out->push_back(download_task);
    out->push_back(remove_task);
    out->push_back(create_task);
}

//////////////////////////////////////////////线程池////////////////////////////////////////////////
//先描述，再组织
class thread_pool
{
public:
    thread_pool(int pid, int fd_id):pid_(pid), fd_id_(fd_id)
    {
        char buffer[1024];
        snprintf(buffer, sizeof buffer, "thread_ %d[thread_num:%d, fd_id:%d]", pid, thread_num_, fd_id_);
        name_ = buffer;
        thread_num_++;
    }
public:
    int pid_;//线程id
    std::string name_;//线程名
    int fd_id_;//文件描述符
    static int thread_num_;//线程数
};

int thread_pool::thread_num_ = 0;//初始化线程数

//接收任务码
int recv_task(int read_fd)
{
    int code = 0;
    ssize_t s = read(read_fd, &code, sizeof(code));
    if(s == sizeof(int)) return code;
    else return -1;
}

//发送任务
void send_task(const thread_pool& thread_, /*线程*/task_t t /*任务*/)
{
    std::cout << "process " << thread_.name_ << "is doing task " << t << ": ";
    fflush(stdout);
    int n = write(thread_.fd_id_, &t, sizeof(t));
    assert(n == sizeof(int));
    (void)n;
}

void createThread(std::vector<thread_pool> *out, const std::vector<std::function<void()>>& tv)
{
    for(int i = 0; i < THREAD_NUM; ++i)
    {
        int fds[2];
        int t = pipe(fds);
        assert(t == 0);
        pid_t pid = fork();
        assert(pid >= 0);
        if(pid == 0)
        {
            //子进程读端
            int s = close(fds[1]);
            assert(s == 0);
            (void)s;
            // int cnt = 10;
            while(true/*cnt--*/)
            {
                //获取任务码
                int command_code = recv_task(fds[0]);
                //完成任务
                if(command_code >= 0 && command_code < tv.size())
                {
                    tv[command_code]();
                }
                else if(command_code == -1)
                {
                    break;
                }
                else
                {
                    std::cout << "errno : " << errno << strerror(errno) << std::endl;
                }
                // sleep(1);
            }
            exit(0);//正常退出
        }
        //父进程写端
        int s = close(fds[0]);
        assert(s == 0);
        (void)s;
        thread_pool tp(pid, fds[1]);
        out->push_back(tp);
    }
}

//进程平衡
void load_balance_contrl(const std::vector<thread_pool>& tpv, \
                         const std::vector<std::function<void()>>& tv)
{
    int cnt = 10;
    while(cnt)
    {
        //选择任务
        int task_ = rand() % tv.size();
        
        //选择进程
        int thread_ = rand() % THREAD_NUM;

        //将任务发送给进程
        send_task(tpv[thread_], task_);

        sleep(1);
        cnt--;
    }
    for(int i = THREAD_NUM-1; i >= 0; i--) close(tpv[i].fd_id_);
}

int main()
{
    //获得随机数种子
    get_seed();
    //线程池：先创建5个线程，然后创建任务，然后由子进程去完成任务
    //用vector存放任务
    std::vector<std::function<void()>> tv; 
    load_task(&tv);

    //用vector存放线程池
    std::vector<thread_pool> tpv;
    createThread(&tpv, tv);
    
    load_balance_contrl(tpv, tv);

    //回收子进程
    for(int i = THREAD_NUM-1; i >= 0; i--) waitpid(tpv[i].pid_, nullptr, 0);

#if -Debug
    std::cout << "debug" << std::endl;
    for(int i=0; i<tv.size(); i++)
    {
        tv[i](); 
    }
    std::cout << tpv.size() << std::endl;
#endif
    return 0;
}